<?php

namespace App\Libs\Company\SheetTemplateHandler\Report;

use App\Exceptions\AppException;
use App\Libs\Company\SheetTemplateHandler\Helper;
use App\Models\DataDict;
use App\Models\District;
use App\Models\SheetTemplate;
use App\Models\SubTask;
use App\Services\DictService;
use Illuminate\Support\Facades\Log;

abstract class AbstractTemplate
{
    public $template;

    public $subTask;

    public $output = [];

    public $dataStartRow = 0;

    public $validateType = 1; //1-列验证

    public $rules = [];

    protected $dataEndRow = 0;

    public function __construct(SheetTemplate $template, SubTask $subTask = null)
    {
        $this->template = $template;
        $this->subTask = $subTask;
        if (!$this->dataEndRow) {
            $this->dataEndRow = $this->dataStartRow;
        }
    }

    public function getDistrictName($districtId)
    {
        return str_replace('旅游局', '', District::find($districtId)->name);
    }

    public function renderTemplate()
    {
        $this->renderHeader();
        $this->renderFooter();
        return $this->output;
    }

    protected function renderHeader()
    {
        $this->output = [
            'styles' => $this->template->data['styles'],
            'sheets' => [
                'sheet-01' => [
                    'name' => 'Sheet1',
                    'cellData' => $this->template->data['header'],
                    'mergeData' => $this->template->data['mergeData'],
                    'rowData' => $this->template->data['rowData'],
                    'columnData' => $this->template->data['columnData'],
                ],
            ],
        ];
    }

    protected function renderFooter()
    {
        $currRow = $this->dataEndRow + 1;
        $footerData = [];
        $footerMergeData = [];
        $footerRowData = [];

        foreach ($this->template->data['footer'] as $key => $row) {
            $footerData[$currRow] = $row;
            if (isset($row['mergeData'])) {
                $footerMergeData[] = array_merge([
                    'startRow' => $currRow,
                    'endRow' => $currRow,
                ], $row['mergeData']);
            }
            if (isset($row['rowData'])) {
                $footerRowData[$currRow] = $row['rowData'];
            }
            foreach ($footerData[$currRow] as $key => &$cell) {
                if (empty($cell['sum'])) {
                    continue;
                }
                $column = Helper::stringFromColumnIndex($key + 1);
                if ($this->dataEndRow < $this->dataStartRow) {
                    $cell['v'] = '0';
                } else {
                    $cell['f'] = "=IFERROR(SUM({$column}" . $this->dataStartRow + 1 . ":{$column}" . $this->dataEndRow + 1 . "),0)";
                }
                unset($cell['sum']);
            }
            $currRow++;
        }
        $this->output['sheets']['sheet-01']['cellData'] += $footerData;
        $this->output['sheets']['sheet-01']['mergeData'] = array_merge($this->output['sheets']['sheet-01']['mergeData'], $footerMergeData);
        $this->output['sheets']['sheet-01']['rowData'] += $footerRowData;
    }

    public function setSubTask(SubTask $subTask)
    {
        $this->subTask = $subTask;
    }

    public function renderReport()
    {
        if (!$this->subTask) {
            throw new AppException('需要设置工单');
        }
        return $this->generateReport();
    }

    public function getData()
    {
        if (empty($this->subTask) || $this->subTask->status != SubTask::STATUS_COMPLETED) {
            throw new \Exception('工单状态不正确 id:' . $this->subTask->id);
        }
        $data = [];
        $this->dataEndRow = $this->dataStartRow;
        try {
            while (1) {
                $row = $this->subTask->data['sheets']['sheet-01']['cellData'][$this->dataEndRow++] ?? [];
                if (!isset($row[1]['v']) || $row[1]['v'] == '') {
                    break;
                }
                $data[] = $row;
            }
        } catch (\Throwable $th) {
            Log::error('填报表异常 id:' . $this->subTask->id);
            throw $th;
        }
        $this->dataEndRow--;
        return $data;
    }

    /**
     * 填报工单验证
     * @param $data
     * @param int $submit 0-暂存 1-提交
     * @return array|bool
     */
    public function validateReport($data, $submit = 1)
    {
        $errData = [];
        foreach ($this->rules as $k => $v) {
            if ($k == 'column' && !empty($v)) {
                $result = $this->validateColumnData($data, $submit);
                if (is_array($result) && !empty($result)) {
                    $errData = array_merge($errData, $result);
                }
            }
            if ($k == 'cell' && !empty($v)) {
                $result = $this->validateCellData($data, $submit);
                if (is_array($result) && !empty($result)) {
                    $errData = array_merge($errData, $result);
                }
            }
            if ($k == 'dict' && !empty($v)) {
                $result = $this->validateDictData($data, $submit);
                if (is_array($result) && !empty($result)) {
                    $errData = array_merge($errData, $result);
                }
            }
        }

        if (method_exists($this, 'selfValidate')) {
            $selfErrData = $this->selfValidate($data, $submit);
            $errData = array_merge($errData, $selfErrData);
        }

        if (!empty($errData)) {
            return $errData;
        }

        return true;
    }

    /**
     * 获取最后一行索引
     */
    private function getEndRow($rule, $cellData)
    {
        $endRow = 0;
        if (empty($rule['end_row'])) {
            //验证到到最后一行
            $endRow = max(array_keys($cellData)) + 1;
        } elseif (!is_array($rule['end_row'])) {
            $endRow = $rule['end_row'];
        } else {
            $endRowColumn = $rule['end_row']['column'];
            foreach ($cellData as $rowKey => $rowData) {
                if (isset($rowData[$endRowColumn]['v']) && strpos($rowData[$endRowColumn]['v'], $rule['end_row']['str']) !== false) {
                    $endRow = $rowKey;
                    break;
                }
            }
        }

        return $endRow;
    }

    /**
     * 列验证
     */
    public function validateColumnData($data, $submit = 1)
    {
        $cellData = $data['sheets']['sheet-01']['cellData'];
        $rule = $this->rules['column'];
        $endRow = $this->getEndRow($rule, $cellData);

        $errData = [];
        $columns = array_column($rule['rule'], 'column');
        $ruleKeyByColumn = array_column($rule['rule'], null, 'column');;

        for ($row = $rule['start_row']; $row < $endRow; $row++) {
            foreach ($columns as $column) {
                $val = isset($cellData[$row][$column]['v']) ? trim($cellData[$row][$column]['v']) : '';
                $errMsg = '';
                if ($submit && $val === '') {
                    //提交情况验证必填
                    $errMsg = '未填写';
                } elseif ($val !== '') {
                    $errMsg = $this->validateVal($val, $ruleKeyByColumn[$column], $cellData);
                }

                if (!empty($errMsg)) {
                    $rowStr = $row + 1;
                    $columnStr = Helper::stringFromColumnIndex($column + 1);
                    $errData[] = ['row' => $row, 'column' => $column, 'msg' => "{$columnStr}{$rowStr}{$errMsg}"];
                }
            }
        }

        if (!empty($errData)) {
            return $errData;
        }

        return true;
    }

    /**
     * 单元格验证
     */
    public function validateCellData($data, $submit = 1)
    {
        $cellData = $data['sheets']['sheet-01']['cellData'];
        $errData = [];
        $rule = $this->rules['cell']['rule'];
        foreach ($rule as $v) {
            $row = $v['row'];
            $column = $v['column'];
            $val = isset($cellData[$row][$column]['v']) ? trim($cellData[$row][$column]['v']) : '';
            $errMsg = '';
            if ($submit && $val === '') {
                //提交情况验证必填
                $errMsg = '未填写';
            } elseif ($val !== '') {
                $errMsg = $this->validateVal($val, $v, $cellData);
            }

            if (!empty($errMsg)) {
                $rowStr = $row + 1;
                $columnStr = Helper::stringFromColumnIndex($column + 1);
                $errData[] = ['row' => $row, 'column' => $column, 'msg' => "{$columnStr}{$rowStr}{$errMsg}"];
            }
        }
        if (!empty($errData)) {
            return $errData;
        }

        return true;
    }

    /**
     * 字典值完整性验证 vt 1-字典值 2-自定义数组
     */
    public function validateDictData($data)
    {
        $errData = [];
        $rule = $this->rules['dict'];

        $column = $rule['rule']['column'];
        $cellData = $data['sheets']['sheet-01']['cellData'];
        $endRow = $this->getEndRow($rule, $cellData);
        $reportDicts = [];
        for ($row = $rule['start_row']; $row < $endRow; $row++) {
            if (isset($cellData[$row][$column]['v'])) {
                $reportDicts[] = trim($cellData[$row][$column]['v']);
            }
        }

        if ($rule['rule']['vt'] == 2) {
            $dicts = $rule['rule']['vt_val'];
        } else {
            $dicts = DictService::getList($rule['rule']['vt_val'], $this->subTask->district_id)->pluck('dict_code')->toArray();
        }

        $diff = array_diff($dicts, $reportDicts);
        if (!empty($diff)) {
            $errMsg = '字典值缺失--' . implode(',', $diff);
            $errData[] = ['row' => -1, 'column' => $column, 'msg' => $errMsg];
        }
        $diff = array_diff($reportDicts, $dicts);
        if (!empty($diff)) {
            $errMsg = '不在字典值范围--' . implode(',', $diff);
            $errData[] = ['row' => -1, 'column' => $column, 'msg' => $errMsg];
        }

        if (!empty($errData)) {
            return $errData;
        }
        return true;
    }

    //rule vt 1-整数 2-2位小数 3-字典值 4-自定义字典值 5-公式
    public function validateVal($val, $rule, $cellData = [])
    {
        $errMsg = '';
        if (empty($val)) {
            return $errMsg;
        }
        switch ($rule['vt']) {
            case 1:
                if (!preg_match('/^[0-9]+$/', $val)) {
                    $errMsg = '必须是整数';
                }
                break;
            case 2:
                if (!preg_match('/^[0-9]+(.[0-9]{1,2})?$/', $val)) {
                    $errMsg = '必须是2位小数';
                }
                break;
            case 3:
                $model = DataDict::where(['status' => DataDict::STATUS_ENABLED, 'category_code' => $rule['vt_val'], 'dict_code' => $val])->first();
                if (empty($model)) {
                    $errMsg = '字典值不正确';
                }
                break;
            case 4:
                if (!in_array($val, $rule['vt_val'])) {
                    $errMsg = '取值不正确';
                }
                break;
            case 5:
                if ($rule['vt_val'][0] == 'SUM') {
                    $sum = 0;
                    foreach ($rule['vt_val'][1] as $v) {
                        if (!isset($cellData[$v[0]][$v[1]]['v'])) {
                            $errMsg = '公式单元格被修改';
                            break;
                        }
                        $sum += $cellData[$v[0]][$v[1]]['v'];

                    }

                    if (bccomp($sum, $val, 2) != 0) {
                        $errMsg = '公式单元格被修改';
                    }
                }
                break;
            default:
                throw new AppException('vt值不正确');
        }

        if (!empty($errMsg)) {
            return $errMsg;
        }

        return '';
    }

    abstract public function generateReport();

    // 仅支持双行数据
    public function createTitleCell($text)
    {
        $text = trim($text, "\r\n");
        $len = mb_strlen($text);

        return [
            'p' => [
                'id' => 'd',
                'body' => [
                    'textRuns' => [
                        [
                            'ed' => $len,
                            'st' => 0,
                            'ts' => ['fs' => 16]
                        ]
                    ],
                    'dataStream' => $text . "\r\n",
                    'paragraphs' => [
                        ['startIndex' => mb_strpos($text, "\r"), 'paragraphStyle' => ['horizontalAlign' => 2]],
                        ['startIndex' => $len, 'paragraphStyle' => ['horizontalAlign' => 2]]
                    ],
                    'sectionBreaks' => [
                        ['startIndex' => $len + 1]
                    ]
                ],
            ],
            's' => 'TITLE'
        ];
    }
}
